/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.

  Keepass2Android is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Keepass2Android is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>.
  */

using System;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Database;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Content.PM;
using Java.IO;
using KeePassLib.Serialization;
using Keepass2android.Pluginsdk;
using keepass2android.Io;
using keepass2android;
using Console = System.Console;
using Environment = Android.OS.Environment;
using Google.Android.Material.AppBar;
using Android.Util;
using keepass2android.Utils;

namespace keepass2android
{


  /// <summary>
  /// Activity to select the file to use
  /// </summary>
  [Activity(Label = "@string/app_name",
             ConfigurationChanges = ConfigChanges.Orientation |
             ConfigChanges.KeyboardHidden,
             Theme = "@style/Kp2aTheme_BlueNoActionBar")]
  public class FileSelectActivity : AndroidX.AppCompat.App.AppCompatActivity
  {
    private readonly ActivityDesign _design;
    public FileSelectActivity(IntPtr javaReference, JniHandleOwnership transfer)
        : base(javaReference, transfer)
    {
      _design = new ActivityDesign(this);
    }

    public FileSelectActivity()
    {
      _design = new ActivityDesign(this);
    }

    private const int CmenuClear = Menu.First;

    const string BundleKeyRecentMode = "RecentMode";

    private FileDbHelper _dbHelper;

    private bool _recentMode;


    private const int RequestCodeSelectIoc = 456;
    private const int RequestCodeEditIoc = 457;

    public const string NoForwardToPasswordActivity = "NoForwardToPasswordActivity";

    protected override void OnCreate(Bundle savedInstanceState)
    {
      _design.ApplyTheme();
      base.OnCreate(savedInstanceState);


      Kp2aLog.Log("FileSelect.OnCreate");



      _dbHelper = App.Kp2a.FileDbHelper;
      SetContentView(Resource.Layout.file_selection);

      var collapsingToolbar = FindViewById<CollapsingToolbarLayout>(Resource.Id.collapsing_toolbar);
      collapsingToolbar.Title = "";
      SetSupportActionBar(FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.toolbar));
      SupportActionBar.Title = "";


      if (ShowRecentFiles())
      {
        _recentMode = true;


        FindViewById(Resource.Id.recent_files).Visibility = ViewStates.Visible;
        Android.Util.Log.Debug("KP2A", "Recent files visible");

      }
      else
      {
        FindViewById(Resource.Id.recent_files).Visibility = ViewStates.Invisible;
        Android.Util.Log.Debug("KP2A", "Recent files invisible");
#if NoNet
				ImageView imgView = FindViewById(Resource.Id.splashlogo) as ImageView;
				if (imgView != null)
				{
					imgView.SetImageDrawable(Resources.GetDrawable(Resource.Drawable.splashlogo_offline));
				}
#endif
      }

      Button openFileButton = (Button)FindViewById(Resource.Id.start_open_file);

      EventHandler openFileButtonClick = (sender, e) =>
      {
        Intent intent = new Intent(this, typeof(SelectStorageLocationActivity));
        intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppGet, true);
        intent.PutExtra(FileStorageSelectionActivity.AllowThirdPartyAppSend, false);
        intent.PutExtra(SelectStorageLocationActivity.ExtraKeyWritableRequirements, (int)SelectStorageLocationActivity.WritableRequirements.WriteDesired);
        intent.PutExtra(FileStorageSetupDefs.ExtraIsForSave, false);
        StartActivityForResult(intent, RequestCodeSelectIoc);
      };
      openFileButton.Click += openFileButtonClick;

      //CREATE NEW
      Button createNewButton = (Button)FindViewById(Resource.Id.start_create);
      EventHandler createNewButtonClick = (sender, e) =>
          {
            //ShowFilenameDialog(false, true, true, Android.OS.Environment.ExternalStorageDirectory + GetString(Resource.String.default_file_path), "", Intents.RequestCodeFileBrowseForCreate)
            Intent i = new Intent(this, typeof(CreateDatabaseActivity));
            i.PutExtra("MakeCurrent", Intent.GetBooleanExtra("MakeCurrent", true));

            i.SetFlags(ActivityFlags.ForwardResult);
            StartActivity(i);
            Finish();
          };
      createNewButton.Click += createNewButtonClick;

      /*//CREATE + IMPORT
      Button createImportButton = (Button)FindViewById(Resource.Id.start_create_import);
      createImportButton.Click += (object sender, EventArgs e) => 
      {
          openButton.Visibility = ViewStates.Gone;
          createButton.Visibility = ViewStates.Visible;
          enterFilenameDetails.Text = GetString(Resource.String.enter_filename_details_create_import);
          enterFilenameDetails.Visibility = enterFilenameDetails.Text == "" ? ViewStates.Gone : ViewStates.Visible;
          // Set the initial value of the filename
          EditText filename = (EditText)FindViewById(Resource.Id.file_filename);
          filename.Text = Android.OS.Environment.ExternalStorageDirectory + GetString(Resource.String.default_file_path);

      };*/

      FindViewById<Switch>(Resource.Id.local_backups_switch).CheckedChange += (sender, args) => { FillData(); };

      FillData();

      if (savedInstanceState != null)
      {
        _recentMode = savedInstanceState.GetBoolean(BundleKeyRecentMode, _recentMode);
      }

    }

    private bool ShowRecentFiles()
    {
      if (!RememberRecentFiles())
      {
        _dbHelper.DeleteAll();
      }

      return _dbHelper.HasRecentFiles();
    }

    private bool RememberRecentFiles()
    {
      return PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(GetString(Resource.String.RememberRecentFiles_key), Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default));
    }


    protected override void OnSaveInstanceState(Bundle outState)
    {
      base.OnSaveInstanceState(outState);

      outState.PutBoolean(BundleKeyRecentMode, _recentMode);

    }

    class MyCursorAdapter : CursorAdapter
    {
      private LayoutInflater cursorInflater;
      private readonly FileSelectActivity _activity;
      private IKp2aApp _app;

      public MyCursorAdapter(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
      {
      }

      public MyCursorAdapter(FileSelectActivity activity, ICursor c, IKp2aApp app) : base(activity, c)
      {
        _activity = activity;
        _app = app;
      }

      public MyCursorAdapter(Context context, ICursor c, bool autoRequery) : base(context, c, autoRequery)
      {
      }

      public MyCursorAdapter(Context context, ICursor c, CursorAdapterFlags flags) : base(context, c, flags)
      {

      }

      public override void BindView(View view, Context context, ICursor cursor)
      {

        String path = cursor.GetString(1);

        TextView textView = view.FindViewById<TextView>(Resource.Id.file_filename);
        IOConnectionInfo ioc = new IOConnectionInfo { Path = path };
        var fileStorage = _app.GetFileStorage(ioc);

        String displayName = cursor.GetString(6);
        if (string.IsNullOrEmpty(displayName))
        {
          displayName = fileStorage.GetDisplayName(ioc);

        }

        textView.Text = displayName;
        textView.Tag = ioc.Path;

      }

      public override View NewView(Context context, ICursor cursor, ViewGroup parent)
      {
        if (cursorInflater == null)
          cursorInflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
        View view = cursorInflater.Inflate(Resource.Layout.file_row, parent, false);

        view.FindViewById(Resource.Id.group_name_vdots).Click += (sender, args) =>
        {
          Handler handler = new Handler(Looper.MainLooper);
          handler.Post(() =>
                  {
                    PopupMenu popupMenu = new PopupMenu(context, view.FindViewById(Resource.Id.group_name_vdots));

                    AccessManager.PreparePopup(popupMenu);
                    int remove = 0;
                    int edit = 1;
                    popupMenu.Menu.Add(0, remove, 0, context.GetString(Resource.String.remove_from_filelist)).SetIcon(Resource.Drawable.baseline_delete_24);

                    TextView textView = view.FindViewById<TextView>(Resource.Id.file_filename);

                    String filename = (string)textView.Tag;
                    IOConnectionInfo ioc = new IOConnectionInfo { Path = filename };
                    if (FileSelectHelper.CanEditIoc(ioc))
                    {
                      popupMenu.Menu.Add(0, edit, 0, context.GetString(Resource.String.edit)).SetIcon(Resource.Drawable.baseline_edit_24);
                    }


                    popupMenu.MenuItemClick += delegate (object sender2, PopupMenu.MenuItemClickEventArgs args2)
                            {
                              if (args2.Item.ItemId == remove)
                              {
                                if (new LocalFileStorage(App.Kp2a).IsLocalBackup(IOConnectionInfo.FromPath(filename)))
                                {
                                  try
                                  {
                                    Java.IO.File file = new Java.IO.File(filename);
                                    file.Delete();
                                  }
                                  catch (Exception exception)
                                  {
                                    Kp2aLog.LogUnexpectedError(exception);
                                  }
                                }

                                App.Kp2a.FileDbHelper.DeleteFile(filename);

                                cursor.Requery();
                              }
                              if (args2.Item.ItemId == edit)
                              {
                                var fsh = new FileSelectHelper(_activity, false, false, RequestCodeEditIoc);
                                fsh.OnOpen += (o, newConnectionInfo) =>
                                  {
                                    _activity.EditFileEntry(filename, newConnectionInfo);
                                  };
                                fsh.PerformManualFileSelect(filename);

                              }
                            };
                    popupMenu.Show();
                  });
        };

        view.FindViewById(Resource.Id.file_filename).Click += (sender, args) =>
        {
          TextView textView = view.FindViewById<TextView>(Resource.Id.file_filename);

          String filename = (string)textView.Tag;
          IOConnectionInfo ioc = new IOConnectionInfo { Path = filename };

          App.Kp2a.GetFileStorage(ioc)
                      .PrepareFileUsage(new FileStorageSetupInitiatorActivity(_activity, _activity.OnActivityResult, null), ioc, 0, false);
        };

        return view;
      }


    }

    private void EditFileEntry(string filename, IOConnectionInfo newConnectionInfo)
    {
      try
      {
        App.Kp2a.GetFileStorage(newConnectionInfo);
      }
      catch (NoFileStorageFoundException)
      {
        App.Kp2a.ShowMessage(this, "Don't know how to handle " + newConnectionInfo.Path, MessageSeverity.Error);
        return;
      }

      _dbHelper.CreateFile(newConnectionInfo, _dbHelper.GetKeyFileForFile(filename), false);
      _dbHelper.DeleteFile(filename);

      LaunchPasswordActivityForIoc(newConnectionInfo);

    }


    private void FillData()
    {
      // Get all of the rows from the database and create the item list
      ICursor filesCursor = _dbHelper.FetchAllFiles();



      if (FindViewById<Switch>(Resource.Id.local_backups_switch).Checked == false)
      {
        var fileStorage = new LocalFileStorage(App.Kp2a);
        filesCursor = new FilteredCursor(filesCursor, cursor => !fileStorage.IsLocalBackup(IOConnectionInfo.FromPath(cursor.GetString(1))));
      }

      StartManagingCursor(filesCursor);

      FragmentManager.FindFragmentById<RecentFilesFragment>(Resource.Id.recent_files).SetAdapter(new MyCursorAdapter(this, filesCursor, App.Kp2a));


    }


    void LaunchPasswordActivityForIoc(IOConnectionInfo ioc)
    {
      IFileStorage fileStorage = App.Kp2a.GetFileStorage(ioc);

      if (fileStorage.RequiresCredentials(ioc))
      {
        Util.QueryCredentials(ioc, AfterQueryCredentials, this);
      }
      else
      {
        try
        {
          PasswordActivity.Launch(this, ioc, new ActivityLaunchModeForward(), Intent.GetBooleanExtra("MakeCurrent", true));
          Finish();
        }
        catch (Java.IO.FileNotFoundException)
        {
          App.Kp2a.ShowMessage(this, Resource.String.FileNotFound, MessageSeverity.Error);
        }
      }
    }



    private void AfterQueryCredentials(IOConnectionInfo ioc)
    {
      PasswordActivity.Launch(this, ioc, new ActivityLaunchModeForward(), Intent.GetBooleanExtra("MakeCurrent", true));
      Finish();
    }


    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
      base.OnActivityResult(requestCode, resultCode, data);

      if (resultCode == KeePass.ExitCloseAfterTaskComplete)
      {
        //no need to set the result ExitCloseAfterTaskComplete here, there's no parent Activity on the stack
        Finish();
        return;
      }

      FillData();


      if (resultCode == (Result)FileStorageResults.FileUsagePrepared)
      {
        IOConnectionInfo ioc = new IOConnectionInfo();
        Util.SetIoConnectionFromIntent(ioc, data);
        LaunchPasswordActivityForIoc(ioc);
      }

      if ((resultCode == Result.Ok) && (requestCode == RequestCodeSelectIoc))
      {
        IOConnectionInfo ioc = new IOConnectionInfo();
        Util.SetIoConnectionFromIntent(ioc, data);
        LaunchPasswordActivityForIoc(ioc);
      }

      if ((resultCode == Result.Ok) && (requestCode == RequestCodeEditIoc))
      {
        string filename = Util.IntentToFilename(data, this);

        LaunchPasswordActivityForIoc(IOConnectionInfo.FromPath(filename));
      }

    }

    protected override void OnResume()
    {
      base.OnResume();
      App.Kp2a.OfflineMode = false; //no matter what the preferences are, file selection or db creation is performed offline. PasswordActivity might set this to true.
      Kp2aLog.Log("FileSelect.OnResume");
      App.Kp2a.MessagePresenter = new ChainedSnackbarPresenter(FindViewById(Resource.Id.main_content));

      _design.ReapplyTheme();

      // Check to see if we need to change modes
      if (ShowRecentFiles() != _recentMode)
      {
        // Restart the activity
        Recreate();
        return;
      }



    }

    protected override void OnStart()
    {

      try
      {
        base.OnStart();
        Kp2aLog.Log("FileSelect.OnStart");


        //if no database is loaded: load the most recent database
        if ((Intent.GetBooleanExtra(NoForwardToPasswordActivity, false) == false) && _dbHelper.HasRecentFiles() &&
            !App.Kp2a.OpenDatabases.Any())
        {
          var fileStorage = new LocalFileStorage(App.Kp2a);
          ICursor filesCursor = _dbHelper.FetchAllFiles();
          filesCursor = new FilteredCursor(filesCursor,
              cursor => !fileStorage.IsLocalBackup(IOConnectionInfo.FromPath(cursor.GetString(1))));
          StartManagingCursor(filesCursor);
          if (filesCursor.Count > 0)
          {
            filesCursor.MoveToFirst();
            IOConnectionInfo ioc = _dbHelper.CursorToIoc(filesCursor);
            if (App.Kp2a.GetFileStorage(ioc).RequiresSetup(ioc) == false)
            {
              LaunchPasswordActivityForIoc(ioc);
            }
            else
            {
              App.Kp2a.GetFileStorage(ioc)
                  .PrepareFileUsage(new FileStorageSetupInitiatorActivity(this, OnActivityResult, null), ioc,
                      0, false);
            }
          }
        }
      }
      catch (Exception e)
      {
        Kp2aLog.LogUnexpectedError(e);
        App.Kp2a.ShowMessage(this, "Error: " + Util.GetErrorMessage(e), MessageSeverity.Error);
        Finish();
      }


    }
    public override bool OnCreateOptionsMenu(IMenu menu)
    {
      base.OnCreateOptionsMenu(menu);

      MenuInflater inflater = MenuInflater;
      inflater.Inflate(Resource.Menu.fileselect, menu);

      return true;
    }

    protected override void OnPause()
    {
      base.OnPause();
      App.Kp2a.MessagePresenter = new NonePresenter();
      Kp2aLog.Log("FileSelect.OnPause");
    }

    protected override void OnDestroy()
    {
      base.OnDestroy();
      GC.Collect();
      Kp2aLog.Log("FileSelect.OnDestroy" + IsFinishing.ToString());
    }

    protected override void OnStop()
    {
      base.OnStop();
      Kp2aLog.Log("FileSelect.OnStop");
    }

    public override bool OnOptionsItemSelected(IMenuItem item)
    {
      switch (item.ItemId)
      {
        case Resource.Id.menu_donate:
          return Util.GotoDonateUrl(this);
        case Resource.Id.menu_about:
          AboutDialog dialog = new AboutDialog(this);
          dialog.Show();
          return true;

        case Resource.Id.menu_app_settings:
          AppSettingsActivity.Launch(this);
          return true;
      }

      return base.OnOptionsItemSelected(item);
    }

  }


  public class NonScrollListView : ListView
  {
    public NonScrollListView(Context context) : base(context)
    {
    }

    public NonScrollListView(Context context, IAttributeSet attrs) : base(context, attrs)
    {
    }

    public NonScrollListView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
    {
    }

    protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
      // Custom height measure spec to make ListView non-scrollable
      int heightMeasureSpecCustom = MeasureSpec.MakeMeasureSpec(int.MaxValue >> 2, MeasureSpecMode.AtMost);
      base.OnMeasure(widthMeasureSpec, heightMeasureSpecCustom);

      // Set the height of the ListView to the measured height
      ViewGroup.LayoutParams layoutParams = LayoutParameters;
      if (layoutParams != null)
      {
        layoutParams.Height = MeasuredHeight;
      }
    }
  }

  public class RecentFilesFragment : ListFragment
  {
    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
      var view = inflater.Inflate(Resource.Layout.recent_files, container, false);
      Android.Util.Log.Debug("KP2A", "OnCreateView");
      return view;
    }

    public void SetAdapter(BaseAdapter adapter)
    {
      ListAdapter = adapter;
      Android.Util.Log.Debug("KP2A", "SetAdapter");
    }

    public override void OnActivityCreated(Bundle savedInstanceState)
    {
      base.OnActivityCreated(savedInstanceState);
      Android.Util.Log.Debug("KP2A", "OnActCreated");

      RefreshList();
      RegisterForContextMenu(ListView);

    }

    public void RefreshList()
    {
      Android.Util.Log.Debug("KP2A", "RefreshList");
      CursorAdapter ca = (CursorAdapter)ListAdapter;
      ICursor cursor = ca.Cursor;
      cursor.Requery();
    }



  }
}

